ํ์ ์์ ์ฑ ๊ถํ ๋ถ์ฌ์ ๋ํ ํฌ๊ด์ ์ธ ๊ฐ์ด๋๋ฅผ ํตํด ๊ฐ๋ ฅํ ์ ํ๋ฆฌ์ผ์ด์ ๋ณด์์ ํ๋ณดํ์ธ์. ๋ฒ๊ทธ ๋ฐฉ์ง, ๊ฐ๋ฐ์ ๊ฒฝํ ํฅ์, ํ์ฅ ๊ฐ๋ฅํ ์ ๊ทผ ์ ์ด ๊ตฌ์ถ ๋ฐฉ๋ฒ์ ๋ฐฐ์๋๋ค.
์ฝ๋ ๊ฐํ: ํ์ ์์ ์ฑ ๊ถํ ๋ถ์ฌ ๋ฐ ๊ถํ ๊ด๋ฆฌ ์ฌ์ธต ๋ถ์
๋ณต์กํ ์ํํธ์จ์ด ๊ฐ๋ฐ์ ์ธ๊ณ์์ ๋ณด์์ ๊ธฐ๋ฅ์ด ์๋๋ผ ๊ทผ๋ณธ์ ์ธ ์๊ตฌ์ฌํญ์ ๋๋ค. ์ฐ๋ฆฌ๋ ๋ฐฉํ๋ฒฝ์ ๊ตฌ์ถํ๊ณ , ๋ฐ์ดํฐ๋ฅผ ์ํธํํ๋ฉฐ, ์ฃผ์ ๊ณต๊ฒฉ์ผ๋ก๋ถํฐ ๋ณดํธํฉ๋๋ค. ํ์ง๋ง ํํ๊ณ ์๋ฐํ ์ทจ์ฝ์ ์ ์ข ์ข ๋์ ๋๊ฒ, ์ฆ ์ ํ๋ฆฌ์ผ์ด์ ๋ก์ง ๊น์ํ ๊ณณ์ ์จ์ด ์์ต๋๋ค: ๋ฐ๋ก ๊ถํ ๋ถ์ฌ์ ๋๋ค. ํนํ, ๊ถํ์ ๊ด๋ฆฌํ๋ ๋ฐฉ์์ด ๊ทธ๋ ์ต๋๋ค. ์๋ ๋์ ๊ฐ๋ฐ์๋ค์ ๊ฒ์ผ๋ก๋ ๋ฌดํดํด ๋ณด์ด๋ ํจํด์ธ ๋ฌธ์์ด ๊ธฐ๋ฐ ๊ถํ์ ์์กดํด์์ต๋๋ค. ์ด ๋ฐฉ์์ ์์ํ๊ธฐ๋ ๊ฐ๋จํ์ง๋ง, ์ข ์ข ์ทจ์ฝํ๊ณ ์ค๋ฅ๊ฐ ๋ฐ์ํ๊ธฐ ์ฌ์ฐ๋ฉฐ ์์ ํ์ง ์์ ์์คํ ์ผ๋ก ์ด์ด์ง๋๋ค. ๊ฐ๋ฐ ๋๊ตฌ๋ฅผ ํ์ฉํ์ฌ ๊ถํ ๋ถ์ฌ ์ค๋ฅ๊ฐ ํ๋ก๋์ ์ ๋๋ฌํ๊ธฐ ์ ์ ์ก์๋ผ ์ ์๋ค๋ฉด ์ด๋จ๊น์? ์ปดํ์ผ๋ฌ ์์ฒด๊ฐ ์ฐ๋ฆฌ์ ์ฒซ ๋ฒ์งธ ๋ฐฉ์ด์ ์ด ๋ ์ ์๋ค๋ฉด ์ด๋จ๊น์? ํ์ ์์ ์ฑ ๊ถํ ๋ถ์ฌ์ ์ธ๊ณ์ ์ค์ ๊ฒ์ ํ์ํฉ๋๋ค.
์ด ๊ฐ์ด๋๋ ๋ฌธ์์ด ๊ธฐ๋ฐ ๊ถํ์ ์ทจ์ฝํ ์ธ๊ณ์์ ๋ฒ์ด๋ ๊ฐ๋ ฅํ๊ณ ์ ์ง๋ณด์ ๊ฐ๋ฅํ๋ฉฐ ๋งค์ฐ ์์ ํ ํ์ ์์ ์ฑ ๊ถํ ๋ถ์ฌ ์์คํ ์ ๊ตฌ์ถํ๋ ํฌ๊ด์ ์ธ ์ฌ์ ์ผ๋ก ์ฌ๋ฌ๋ถ์ ์๋ดํ ๊ฒ์ ๋๋ค. ์ฐ๋ฆฌ๋ TypeScript์ ์ค์ ์์๋ฅผ ์ฌ์ฉํ์ฌ '์', '๋ฌด์์', '์ด๋ป๊ฒ'๋ฅผ ํ๊ตฌํ ๊ฒ์ด๋ฉฐ, ์ด๋ ๋ชจ๋ ์ ์ ํ์ ์ธ์ด์ ์ ์ฉํ ์ ์๋ ๊ฐ๋ ์ ์ค๋ช ํฉ๋๋ค. ์ด ๊ฐ์ด๋๊ฐ ๋๋ ๋์ฏค์ด๋ฉด ์ด๋ก ์ ์ดํดํ ๋ฟ๋ง ์๋๋ผ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ณด์ ์ํ๋ฅผ ๊ฐํํ๊ณ ๊ฐ๋ฐ์ ๊ฒฝํ์ ํฅ์์ํค๋ ๊ถํ ๊ด๋ฆฌ ์์คํ ์ ๊ตฌํํ ์ค์ง์ ์ธ ์ง์์ ๊ฐ๊ฒ ๋ ๊ฒ์ ๋๋ค.
๋ฌธ์์ด ๊ธฐ๋ฐ ๊ถํ์ ์ทจ์ฝ์ฑ: ํํ ํจ์
๋ณธ์ง์ ์ผ๋ก ๊ถํ ๋ถ์ฌ๋ "์ด ์ฌ์ฉ์๊ฐ ์ด ์์
์ ์ํํ ๊ถํ์ด ์๋๊ฐ?"๋ผ๋ ๊ฐ๋จํ ์ง๋ฌธ์ ๋ตํ๋ ๊ฒ์
๋๋ค. ๊ถํ์ ๋ํ๋ด๋ ๊ฐ์ฅ ๊ฐ๋จํ ๋ฐฉ๋ฒ์ "edit_post" ๋๋ "delete_user"์ ๊ฐ์ ๋ฌธ์์ด์ ์ฌ์ฉํ๋ ๊ฒ์
๋๋ค. ์ด๋ ๋ค์๊ณผ ๊ฐ์ ์ฝ๋๋ก ์ด์ด์ง๋๋ค:
if (user.hasPermission("create_product")) { ... }
์ด ์ ๊ทผ ๋ฐฉ์์ ์ฒ์์๋ ๊ตฌํํ๊ธฐ ์ฝ์ง๋ง, ๋ง์น ๋ชจ๋์ฑ ๊ฐ์ต๋๋ค. ์ข ์ข "๋งค์ง ๋ฌธ์์ด"์ ์ฌ์ฉํ๋ ๊ฒ์ผ๋ก ๋ถ๋ฆฌ๋ ์ด ๊ดํ์ ์๋นํ ์์ ์ํ๊ณผ ๊ธฐ์ ๋ถ์ฑ๋ฅผ ์ด๋ํฉ๋๋ค. ์ด ํจํด์ด ์ ๊ทธ๋ ๊ฒ ๋ฌธ์ ๊ฐ ๋ง์์ง ๋ถ์ํด ๋ด ์๋ค.
์ค๋ฅ์ ์ฐ์
- ์ ์ํ ์คํ: ์ด๊ฒ์ด ๊ฐ์ฅ ๋ถ๋ช
ํ ๋ฌธ์ ์
๋๋ค.
"create_product"๋์"create_pruduct"๋ฅผ ํ์ธํ๋ ๊ฒ๊ณผ ๊ฐ์ ๊ฐ๋จํ ์คํ๋ ์ถฉ๋์ ์ผ์ผํค์ง ์์ต๋๋ค. ๊ฒฝ๊ณ ๋ ๋ฐ์ํ์ง ์์ต๋๋ค. ๊ฒ์ฌ๋ ๋จ์ํ ์กฐ์ฉํ ์คํจํ๋ฉฐ, ์ ๊ทผ ๊ถํ์ด ์์ด์ผ ํ๋ ์ฌ์ฉ์๋ ๊ฑฐ๋ถ๋ฉ๋๋ค. ๋ ๋์ ๊ฒ์ ๊ถํ ์ ์์ ์คํ๊ฐ ์ค์๋ก ํ์ฉ๋์ง ์์์ผ ํ ๊ณณ์ ์ ๊ทผ ๊ถํ์ ๋ถ์ฌํ ์ ์๋ค๋ ๊ฒ์ ๋๋ค. ์ด๋ฌํ ๋ฒ๊ทธ๋ ์ถ์ ํ๊ธฐ๊ฐ ๋งค์ฐ ์ด๋ ต์ต๋๋ค. - ๋ฎ์ ๋ฐ๊ฒฌ ๊ฐ๋ฅ์ฑ: ์๋ก์ด ๊ฐ๋ฐ์๊ฐ ํ์ ํฉ๋ฅํ ๋, ์ด๋ค ๊ถํ์ ์ฌ์ฉํ ์ ์๋์ง ์ด๋ป๊ฒ ์ ์ ์์๊น์? ๊ทธ๋ค์ ๋ชจ๋ ์ฌ์ฉ๋ฒ์ ์ฐพ๊ธฐ ์ํด ์ ์ฒด ์ฝ๋๋ฒ ์ด์ค๋ฅผ ๊ฒ์ํด์ผ ํฉ๋๋ค. ๋จ์ผ ์ง์ค ๊ณต๊ธ์๋, ์๋ ์์ฑ๋, ์ฝ๋ ์์ฒด์์ ์ ๊ณต๋๋ ๋ฌธ์๋ ์์ต๋๋ค.
- ๋ฆฌํฉํ ๋ง ์
๋ชฝ: ์กฐ์ง์์
"edit_post"๋ฅผ"post:update"๋ก ๋ณ๊ฒฝํ๋ ๋ฑ ๋ณด๋ค ๊ตฌ์กฐํ๋ ๋ช ๋ช ๊ท์น์ ์ฑํํ๊ธฐ๋ก ๊ฒฐ์ ํ๋ค๊ณ ์์ํด ๋ณด์ธ์. ์ด๋ ๋ฐฑ์๋, ํ๋ก ํธ์๋, ์ ์ฌ์ ์ผ๋ก ๋ฐ์ดํฐ๋ฒ ์ด์ค ํญ๋ชฉ๊น์ง ์ ์ฒด ์ฝ๋๋ฒ ์ด์ค์ ๊ฑธ์ณ ์ ์ญ์ ์ด๊ณ ๋์๋ฌธ์๋ฅผ ๊ตฌ๋ถํ๋ ๊ฒ์ ๋ฐ ๊ต์ฒด ์์ ์ ํ์๋ก ํฉ๋๋ค. ์ด๋ ๋จ ํ ๋ฒ์ ๋์นจ์ผ๋ก ๊ธฐ๋ฅ์ด ์์๋๊ฑฐ๋ ๋ณด์ ์ทจ์ฝ์ ์ ๋ง๋ค ์ ์๋ ๊ณ ์ํ ์๋ ํ๋ก์ธ์ค์ ๋๋ค. - ์ปดํ์ผ ์๊ฐ ์์ ์ฑ ์์: ๊ทผ๋ณธ์ ์ธ ์ฝ์ ์ ๊ถํ ๋ฌธ์์ด์ ์ ํจ์ฑ์ด ๋ฐํ์์์๋ง ํ์ธ๋๋ค๋ ๊ฒ์
๋๋ค. ์ปดํ์ผ๋ฌ๋ ์ด๋ค ๋ฌธ์์ด์ด ์ ํจํ ๊ถํ์ธ์ง ์๋์ง์ ๋ํด ์ ํ ์์ง ๋ชปํฉ๋๋ค.
"delete_user"์"delete_useeer"๋ฅผ ๋์ผํ๊ฒ ์ ํจํ ๋ฌธ์์ด๋ก ๊ฐ์ฃผํ๋ฉฐ, ์ค๋ฅ ๋ฐ๊ฒฌ์ ์ฌ์ฉ์๋ ํ ์คํธ ๋จ๊ณ๋ก ๋ฏธ๋ฃน๋๋ค.
์คํจ์ ๊ตฌ์ฒด์ ์ธ ์์
๋ฌธ์ ์ ๊ทผ์ ์ ์ดํ๋ ๋ฐฑ์๋ ์๋น์ค๋ฅผ ์๊ฐํด ๋ด
์๋ค. ๋ฌธ์๋ฅผ ์ญ์ ํ๋ ๊ถํ์ "document_delete"๋ก ์ ์๋ฉ๋๋ค.
๊ด๋ฆฌ์ ํจ๋์ ์์ ํ๋ ๊ฐ๋ฐ์๊ฐ ์ญ์ ๋ฒํผ์ ์ถ๊ฐํด์ผ ํฉ๋๋ค. ๊ทธ๋ค์ ๋ค์๊ณผ ๊ฐ์ด ํ์ธ ์ฝ๋๋ฅผ ์์ฑํฉ๋๋ค:
// In the API endpoint
if (currentUser.hasPermission("document:delete")) {
// Proceed with deletion
} else {
return res.status(403).send("Forbidden");
}
๊ฐ๋ฐ์๋ ์๋ก์ด ๊ท์น์ ๋ฐ๋ผ ๋ฐ์ค(_) ๋์ ์ฝ๋ก (:)์ ์ฌ์ฉํ์ต๋๋ค. ์ด ์ฝ๋๋ ๋ฌธ๋ฒ์ ์ผ๋ก ์ฌ๋ฐ๋ฅด๋ฉฐ ๋ชจ๋ ๋ฆฐํธ ๊ท์น์ ํต๊ณผํ ๊ฒ์
๋๋ค. ๊ทธ๋ฌ๋ ๋ฐฐํฌ๋๋ฉด ์ด๋ค ๊ด๋ฆฌ์๋ ๋ฌธ์๋ฅผ ์ญ์ ํ ์ ์๊ฒ ๋ฉ๋๋ค. ๊ธฐ๋ฅ์ ๊ณ ์ฅ ๋ฌ์ง๋ง ์์คํ
์ ์ถฉ๋ํ์ง ์์ต๋๋ค. ๋จ์ํ 403 Forbidden ์ค๋ฅ๋ฅผ ๋ฐํํ ๋ฟ์
๋๋ค. ์ด ๋ฒ๊ทธ๋ ๋ฉฐ์น ๋๋ ๋ช ์ฃผ ๋์ ๋์ ๋์ง ์๊ฒ ์ง๋๊ฐ ์ฌ์ฉ์์๊ฒ ์ข์ ๊ฐ์ ์๊ฒจ์ฃผ๊ณ ๋จ์ผ ๋ฌธ์ ์ค์๋ฅผ ๋ฐํ๋ด๊ธฐ ์ํด ๊ณ ํต์ค๋ฌ์ด ๋๋ฒ๊น
์ธ์
์ ํ์๋ก ํ ์ ์์ต๋๋ค.
์ด๊ฒ์ ์ ๋ฌธ์ ์ธ ์ํํธ์จ์ด๋ฅผ ๊ตฌ์ถํ๋ ์ง์ ๊ฐ๋ฅํ๊ฑฐ๋ ์์ ํ ๋ฐฉ๋ฒ์ด ์๋๋๋ค. ๋ ๋์ ์ ๊ทผ ๋ฐฉ์์ด ํ์ํฉ๋๋ค.
ํ์ ์์ ์ฑ ๊ถํ ๋ถ์ฌ ์๊ฐ: ์ปดํ์ผ๋ฌ๋ฅผ ์ฒซ ๋ฒ์งธ ๋ฐฉ์ด์ ์ผ๋ก
ํ์ ์์ ์ฑ ๊ถํ ๋ถ์ฌ๋ ํจ๋ฌ๋ค์์ ์ ํ์ ๋๋ค. ์ปดํ์ผ๋ฌ๊ฐ ์๋ฌด๊ฒ๋ ๋ชจ๋ฅด๋ ์์์ ๋ฌธ์์ด๋ก ๊ถํ์ ๋ํ๋ด๋ ๋์ , ํ๋ก๊ทธ๋๋ฐ ์ธ์ด์ ํ์ ์์คํ ๋ด์์ ๋ช ์์ ์ธ ํ์ ์ผ๋ก ์ ์ํฉ๋๋ค. ์ด ๊ฐ๋จํ ๋ณ๊ฒฝ์ ๊ถํ ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ๋ฐํ์ ๋ฌธ์ ๊ฐ ์๋ ์ปดํ์ผ ์๊ฐ ๋ณด์ฅ์ผ๋ก ์ด๋์ํต๋๋ค.
ํ์
์์ ์ฑ ์์คํ
์ ์ฌ์ฉํ๋ฉด ์ปดํ์ผ๋ฌ๋ ์ ํจํ ๊ถํ์ ์ ์ฒด ์งํฉ์ ์ดํดํฉ๋๋ค. ์กด์ฌํ์ง ์๋ ๊ถํ์ ํ์ธํ๋ ค๊ณ ํ๋ฉด ์ฝ๋๊ฐ ์ปดํ์ผ์กฐ์ฐจ ๋์ง ์์ต๋๋ค. ์ด์ ์์์ "document:delete"์ "document_delete"์ ์คํ๋ ํ์ผ์ ์ ์ฅํ๊ธฐ๋ ์ ์ ์ฝ๋ ํธ์ง๊ธฐ์์ ์ฆ์ ๋นจ๊ฐ์ ๋ฐ์ค๋ก ํ์๋์ด ๊ฐ์ง๋ ๊ฒ์
๋๋ค.
ํต์ฌ ์์น
- ์ค์ ์ง์ค์ ์ ์: ๋ชจ๋ ๊ฐ๋ฅํ ๊ถํ์ ๋จ์ผ์ ๊ณต์ ๋ ์์น์ ์ ์๋ฉ๋๋ค. ์ด ํ์ผ ๋๋ ๋ชจ๋์ ์ ์ฒด ์ ํ๋ฆฌ์ผ์ด์ ๋ณด์ ๋ชจ๋ธ์ ๋ถ์ธํ ์ ์๋ ์ง์ค์ ์์ฒ์ด ๋ฉ๋๋ค.
- ์ปดํ์ผ ์๊ฐ ๊ฒ์ฆ: ํ์ ์์คํ ์ ๊ฒ์ฌ, ์ญํ ์ ์ ๋๋ UI ๊ตฌ์ฑ ์์ ๋ฑ ๊ถํ์ ๋ํ ๋ชจ๋ ์ฐธ์กฐ๊ฐ ์ ํจํ๊ณ ๊ธฐ์กด์ ๊ถํ์์ ๋ณด์ฅํฉ๋๋ค. ์คํ ๋ฐ ์กด์ฌํ์ง ์๋ ๊ถํ์ ๋ถ๊ฐ๋ฅํฉ๋๋ค.
- ํฅ์๋ ๊ฐ๋ฐ์ ๊ฒฝํ(DX): ๊ฐ๋ฐ์๋ค์
user.hasPermission(...)์ ์ ๋ ฅํ ๋ ์๋ ์์ฑ ๊ฐ์ IDE ๊ธฐ๋ฅ์ ์ป์ต๋๋ค. ์ฌ์ฉ ๊ฐ๋ฅํ ๋ชจ๋ ๊ถํ์ ๋๋กญ๋ค์ด ๋ชฉ๋ก์ ๋ณผ ์ ์์ด ์์คํ ์ด ์์ฒด์ ์ผ๋ก ๋ฌธ์ํ๋๊ณ ์ ํํ ๋ฌธ์์ด ๊ฐ์ ๊ธฐ์ตํด์ผ ํ๋ ์ ์ ์ ๋ถ๋ด์ ์ค์ฌ์ค๋๋ค. - ํ์ ์๋ ๋ฆฌํฉํ ๋ง: ๊ถํ ์ด๋ฆ์ ๋ณ๊ฒฝํด์ผ ํ๋ ๊ฒฝ์ฐ IDE์ ๋ด์ฅ ๋ฆฌํฉํ ๋ง ๋๊ตฌ๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค. ์์ค์์ ๊ถํ ์ด๋ฆ์ ๋ณ๊ฒฝํ๋ฉด ํ๋ก์ ํธ ์ ์ฒด์ ๋ชจ๋ ์ฌ์ฉ์ฒ๊ฐ ์๋์ผ๋ก ์์ ํ๊ฒ ์ ๋ฐ์ดํธ๋ฉ๋๋ค. ํ๋ ๊ณ ์ํ ์๋ ์์ ์ด์๋ ๊ฒ์ด ์ฌ์ํ๊ณ ์์ ํ๋ฉฐ ์๋ํ๋ ์์ ์ด ๋ฉ๋๋ค.
๊ธฐ๋ฐ ๊ตฌ์ถ: ํ์ ์์ ์ฑ ๊ถํ ์์คํ ๊ตฌํ
์ด๋ก ์์ ์ค์ฒ์ผ๋ก ๋์๊ฐ์๋ค. ์ฐ๋ฆฌ๋ ์ฒ์๋ถํฐ ์์ ํ ํ์ ์์ ์ฑ ๊ถํ ์์คํ ์ ๊ตฌ์ถํ ๊ฒ์ ๋๋ค. ์์์์๋ ๊ฐ๋ ฅํ ํ์ ์์คํ ์ด ์ด ์์ ์ ์๋ฒฝํ๊ฒ ์ ํฉํ๊ธฐ ๋๋ฌธ์ TypeScript๋ฅผ ์ฌ์ฉํ ๊ฒ์ ๋๋ค. ๊ทธ๋ฌ๋ ๊ธฐ๋ณธ ์์น์ C#, Java, Swift, Kotlin ๋๋ Rust์ ๊ฐ์ ๋ค๋ฅธ ์ ์ ํ์ ์ธ์ด์ ์ฝ๊ฒ ์ ์ฉ๋ ์ ์์ต๋๋ค.
1๋จ๊ณ: ๊ถํ ์ ์
๊ฐ์ฅ ์ค์ํ๊ณ ์ฒซ ๋ฒ์งธ ๋จ๊ณ๋ ๋ชจ๋ ๊ถํ์ ๋ํ ๋จ์ผ ์ง์ค์ ์์ฒ์ ๋ง๋๋ ๊ฒ์ ๋๋ค. ์ด๋ฅผ ๋ฌ์ฑํ๋ ๋ฐ๋ ์ฌ๋ฌ ๊ฐ์ง ๋ฐฉ๋ฒ์ด ์์ผ๋ฉฐ, ๊ฐ ๋ฐฉ๋ฒ๋ง๋ค ์ฅ๋จ์ ์ด ์์ต๋๋ค.
์ต์ A: ๋ฌธ์์ด ๋ฆฌํฐ๋ด ์ ๋์จ ํ์ ์ฌ์ฉ
์ด๊ฒ์ ๊ฐ์ฅ ๊ฐ๋จํ ์ ๊ทผ ๋ฐฉ์์ ๋๋ค. ๊ฐ๋ฅํ ๋ชจ๋ ๊ถํ ๋ฌธ์์ด์ ์ ๋์จ์ธ ํ์ ์ ์ ์ํฉ๋๋ค. ์๊ท๋ชจ ์ ํ๋ฆฌ์ผ์ด์ ์๋ ๊ฐ๊ฒฐํ๊ณ ํจ๊ณผ์ ์ ๋๋ค.
// src/permissions.ts
export type Permission =
| "user:create"
| "user:read"
| "user:update"
| "user:delete"
| "post:create"
| "post:read"
| "post:update"
| "post:delete";
์ฅ์ : ์์ฑ ๋ฐ ์ดํด๊ฐ ๋งค์ฐ ๊ฐ๋จํฉ๋๋ค.
๋จ์ : ๊ถํ ์๊ฐ ์ฆ๊ฐํจ์ ๋ฐ๋ผ ๋ค๋ฃจ๊ธฐ ์ด๋ ค์์ง ์ ์์ต๋๋ค. ๊ด๋ จ ๊ถํ์ ๊ทธ๋ฃนํํ๋ ๋ฐฉ๋ฒ์ ์ ๊ณตํ์ง ์์ผ๋ฉฐ, ์ฌ์ฉํ ๋ ์ฌ์ ํ ๋ฌธ์์ด์ ์ง์ ์
๋ ฅํด์ผ ํฉ๋๋ค.
์ต์ B: Enum ์ฌ์ฉ
Enum์ ๊ด๋ จ ์์๋ฅผ ๋จ์ผ ์ด๋ฆ ์๋์ ๊ทธ๋ฃนํํ๋ ๋ฐฉ๋ฒ์ ์ ๊ณตํ์ฌ ์ฝ๋๋ฅผ ๋ ์ฝ๊ธฐ ์ฝ๊ฒ ๋ง๋ค ์ ์์ต๋๋ค.
// src/permissions.ts
export enum Permission {
UserCreate = "user:create",
UserRead = "user:read",
UserUpdate = "user:update",
UserDelete = "user:delete",
PostCreate = "post:create",
// ... and so on
}
์ฅ์ : ๋ช
๋ช
๋ ์์(Permission.UserCreate)๋ฅผ ์ ๊ณตํ์ฌ ๊ถํ ์ฌ์ฉ ์ ์คํ๋ฅผ ๋ฐฉ์งํ ์ ์์ต๋๋ค.
๋จ์ : TypeScript enum์ ๋ช ๊ฐ์ง ๋ฏธ๋ฌํ ์ฐจ์ด๊ฐ ์์ผ๋ฉฐ ๋ค๋ฅธ ์ ๊ทผ ๋ฐฉ์๋ณด๋ค ์ ์ฐ์ฑ์ด ๋จ์ด์ง ์ ์์ต๋๋ค. ์ ๋์จ ํ์
์ ๋ํ ๋ฌธ์์ด ๊ฐ์ ์ถ์ถํ๋ ค๋ฉด ์ถ๊ฐ ๋จ๊ณ๊ฐ ํ์ํฉ๋๋ค.
์ต์ C: Object-as-Const ์ ๊ทผ ๋ฐฉ์ (๊ถ์ฅ)
์ด๊ฒ์ ๊ฐ์ฅ ๊ฐ๋ ฅํ๊ณ ํ์ฅ ๊ฐ๋ฅํ ์ ๊ทผ ๋ฐฉ์์ ๋๋ค. ์ฐ๋ฆฌ๋ TypeScript์ `as const` ๋จ์ธ์ ์ฌ์ฉํ์ฌ ๊ถํ์ ๊น๊ฒ ์ค์ฒฉ๋ ์ฝ๊ธฐ ์ ์ฉ ๊ฐ์ฒด๋ก ์ ์ํฉ๋๋ค. ์ด๋ ์กฐ์งํ, ์ ํ๊ธฐ๋ฒ์ ํตํ ๋ฐ๊ฒฌ ๊ฐ๋ฅ์ฑ(์: `Permissions.USER.CREATE`), ๊ทธ๋ฆฌ๊ณ ๋ชจ๋ ๊ถํ ๋ฌธ์์ด์ ์ ๋์จ ํ์ ์ ๋์ ์ผ๋ก ์์ฑํ๋ ๋ฅ๋ ฅ์ด๋ผ๋ ๋ชจ๋ ์ฅ์ ์ ์ ๊ณตํฉ๋๋ค.
์ค์ ๋ฐฉ๋ฒ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
// src/permissions.ts
// 1. Define the permissions object with 'as const'
export const Permissions = {
USER: {
CREATE: "user:create",
READ: "user:read",
UPDATE: "user:update",
DELETE: "user:delete",
},
POST: {
CREATE: "post:create",
READ: "post:read",
UPDATE: "post:update",
DELETE: "post:delete",
},
BILLING: {
READ_INVOICES: "billing:read_invoices",
MANAGE_SUBSCRIPTION: "billing:manage_subscription",
}
} as const;
// 2. Create a helper type to extract all permission values
type TPermissions = typeof Permissions;
// This utility type recursively flattens the nested object values into a union
type FlattenObjectValues
์ด ์ ๊ทผ ๋ฐฉ์์ ์ ํ๋ฆฌ์ผ์ด์ ์ด ์ฑ์ฅํจ์ ๋ฐ๋ผ ๋งค์ฐ ์ค์ํ ๋ช ํํ๊ณ ๊ณ์ธต์ ์ธ ๊ถํ ๊ตฌ์กฐ๋ฅผ ์ ๊ณตํ๋ฏ๋ก ์ฐ์ํฉ๋๋ค. ๊ฒ์ํ๊ธฐ ์ฝ๊ณ , `AllPermissions` ํ์ ์ด ์๋์ผ๋ก ์์ฑ๋๋ฏ๋ก ์ ๋์จ ํ์ ์ ์๋์ผ๋ก ์ ๋ฐ์ดํธํ ํ์๊ฐ ์์ต๋๋ค. ์ด๊ฒ์ ๋๋จธ์ง ์์คํ ์ ์ฌ์ฉํ ๊ธฐ๋ฐ์ ๋๋ค.
2๋จ๊ณ: ์ญํ ์ ์
์ญํ ์ ๋จ์ํ ์ด๋ฆ์ด ์ง์ ๋ ๊ถํ ๋ชจ์์ ๋๋ค. ์ด์ `AllPermissions` ํ์ ์ ์ฌ์ฉํ์ฌ ์ญํ ์ ์๋ ํ์ ์์ ์ฑ์ ๋ณด์ฅํ ์ ์์ต๋๋ค.
// src/roles.ts
import { Permissions, AllPermissions } from './permissions';
// Define the structure for a role
export type Role = {
name: string;
description: string;
permissions: AllPermissions[];
};
// Define a record of all application roles
export const AppRoles: Record
๊ถํ์ ํ ๋นํ๊ธฐ ์ํด `Permissions` ๊ฐ์ฒด(์: `Permissions.POST.READ`)๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ์์ ์ฃผ๋ชฉํ์ธ์. ์ด๋ ์คํ๋ฅผ ๋ฐฉ์งํ๊ณ ์ ํจํ ๊ถํ๋ง ํ ๋นํ๋๋ก ๋ณด์ฅํฉ๋๋ค. `ADMIN` ์ญํ ์ ๊ฒฝ์ฐ, ์๋ก์ด ๊ถํ์ด ์ถ๊ฐ๋ ๋ ๊ด๋ฆฌ์๊ฐ ์๋์ผ๋ก ์ด๋ฅผ ์์ํ๋๋ก ๋ณด์ฅํ๊ธฐ ์ํด `Permissions` ๊ฐ์ฒด๋ฅผ ํ๋ก๊ทธ๋๋ฐ ๋ฐฉ์์ผ๋ก ํํํํ์ฌ ๋ชจ๋ ๊ถํ์ ๋ถ์ฌํฉ๋๋ค.
3๋จ๊ณ: ํ์ ์์ ์ฑ ๊ฒ์ฌ ํจ์ ์์ฑ
์ด๊ฒ์ ์ฐ๋ฆฌ ์์คํ ์ ํต์ฌ์ ๋๋ค. ์ฌ์ฉ์๊ฐ ํน์ ๊ถํ์ ๊ฐ์ง๊ณ ์๋์ง ํ์ธํ ์ ์๋ ํจ์๊ฐ ํ์ํฉ๋๋ค. ํต์ฌ์ ํจ์์ ์๊ทธ๋์ฒ์ ์์ผ๋ฉฐ, ์ด๋ ์ ํจํ ๊ถํ๋ง ๊ฒ์ฌํ ์ ์๋๋ก ๊ฐ์ ํ ๊ฒ์ ๋๋ค.
๋จผ์ , `User` ๊ฐ์ฒด๊ฐ ์ด๋ป๊ฒ ์๊ฒผ๋์ง ์ ์ํด ๋ด ์๋ค:
// src/user.ts
import { AppRoleKey } from './roles';
export type User = {
id: string;
email: string;
roles: AppRoleKey[]; // The user's roles are also type-safe!
};
์ด์ ๊ถํ ๋ถ์ฌ ๋ก์ง์ ๊ตฌ์ถํด ๋ด ์๋ค. ํจ์จ์ฑ์ ์ํด ์ฌ์ฉ์์ ์ ์ฒด ๊ถํ ์งํฉ์ ํ ๋ฒ ๊ณ์ฐํ ๋ค์ ํด๋น ์งํฉ์ ๋ํด ํ์ธํ๋ ๊ฒ์ด ๊ฐ์ฅ ์ข์ต๋๋ค.
// src/authorization.ts
import { User } from './user';
import { AppRoles } from './roles';
import { AllPermissions } from './permissions';
/**
* Computes the complete set of permissions for a given user.
* Uses a Set for efficient O(1) lookups.
* @param user The user object.
* @returns A Set containing all permissions the user has.
*/
function getUserPermissions(user: User): Set
๋ง๋ฒ์ `hasPermission` ํจ์์ `permission: AllPermissions` ๋งค๊ฐ๋ณ์์ ์์ต๋๋ค. ์ด ์๊ทธ๋์ฒ๋ TypeScript ์ปดํ์ผ๋ฌ์๊ฒ ๋ ๋ฒ์งธ ์ธ์๊ฐ ์ฐ๋ฆฌ๊ฐ ์์ฑํ `AllPermissions` ์ ๋์จ ํ์ ์ ๋ฌธ์์ด ์ค ํ๋์ฌ์ผ ํ๋ค๋ ๊ฒ์ ์๋ ค์ค๋๋ค. ๋ค๋ฅธ ๋ฌธ์์ด์ ์ฌ์ฉํ๋ ค๋ ๋ชจ๋ ์๋๋ ์ปดํ์ผ ์๊ฐ ์ค๋ฅ๋ฅผ ๋ฐ์์ํฌ ๊ฒ์ ๋๋ค.
์ค์ ์ฌ์ฉ
์ด๊ฒ์ด ์ฐ๋ฆฌ์ ์ผ์์ ์ธ ์ฝ๋ฉ์ ์ด๋ป๊ฒ ๋ณํ์ํค๋์ง ๋ด ์๋ค. Node.js/Express ์ ํ๋ฆฌ์ผ์ด์ ์์ API ์๋ํฌ์ธํธ๋ฅผ ๋ณดํธํ๋ค๊ณ ์์ํด ๋ณด์ธ์:
import { hasPermission } from './authorization';
import { Permissions } from './permissions';
import { User } from './user';
app.delete('/api/posts/:id', (req, res) => {
const currentUser: User = req.user; // Assume user is attached from auth middleware
// This works perfectly! We get autocomplete for Permissions.POST.DELETE
if (hasPermission(currentUser, Permissions.POST.DELETE)) {
// Logic to delete the post
res.status(200).send({ message: 'Post deleted.' });
} else {
res.status(403).send({ error: 'You do not have permission to delete posts.' });
}
});
// Now, let's try to make a mistake:
app.post('/api/users', (req, res) => {
const currentUser: User = req.user;
// The following line will show a red squiggle in your IDE and FAIL TO COMPILE!
// Error: Argument of type '"user:creat"' is not assignable to parameter of type 'AllPermissions'.
// Did you mean '"user:create"'?
if (hasPermission(currentUser, "user:creat")) { // Typo in 'create'
// This code is unreachable
}
});
์ฐ๋ฆฌ๋ ์ ์ฒด ๋ฒ๊ทธ ๋ฒ์ฃผ๋ฅผ ์ฑ๊ณต์ ์ผ๋ก ์ ๊ฑฐํ์ต๋๋ค. ์ปดํ์ผ๋ฌ๋ ์ด์ ๋ณด์ ๋ชจ๋ธ์ ์ํํ๋ ๋ฐ ์ ๊ทน์ ์ผ๋ก ์ฐธ์ฌํ๊ณ ์์ต๋๋ค.
์์คํ ํ์ฅ: ํ์ ์์ ์ฑ ๊ถํ ๋ถ์ฌ์ ๊ณ ๊ธ ๊ฐ๋
๊ฐ๋จํ ์ญํ ๊ธฐ๋ฐ ์ ๊ทผ ์ ์ด(RBAC) ์์คํ ์ ๊ฐ๋ ฅํ์ง๋ง, ์ค์ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ข ์ข ๋ ๋ณต์กํ ์๊ตฌ ์ฌํญ์ ๊ฐ์ง๋๋ค. ๋ฐ์ดํฐ ์์ฒด์ ๋ฐ๋ผ ๋ฌ๋ผ์ง๋ ๊ถํ์ ์ด๋ป๊ฒ ์ฒ๋ฆฌํ ๊น์? ์๋ฅผ ๋ค์ด, `EDITOR`๋ ๊ฒ์๋ฌผ์ ์ ๋ฐ์ดํธํ ์ ์์ง๋ง, ์์ ์ด ์์ฑํ ๊ฒ์๋ฌผ๋ง ์ ๋ฐ์ดํธํ ์ ์์ต๋๋ค.
์์ฑ ๊ธฐ๋ฐ ์ ๊ทผ ์ ์ด(ABAC) ๋ฐ ๋ฆฌ์์ค ๊ธฐ๋ฐ ๊ถํ
์ฌ๊ธฐ์ ์์ฑ ๊ธฐ๋ฐ ์ ๊ทผ ์ ์ด(ABAC) ๊ฐ๋ ์ ์๊ฐํฉ๋๋ค. ์ฐ๋ฆฌ๋ ์์คํ ์ ํ์ฅํ์ฌ ์ ์ฑ ๋๋ ์กฐ๊ฑด์ ์ฒ๋ฆฌํฉ๋๋ค. ์ฌ์ฉ์๋ ์ผ๋ฐ์ ์ธ ๊ถํ(์: `post:update`)์ ๊ฐ์ง ๋ฟ๋ง ์๋๋ผ, ์ ๊ทผํ๋ ค๋ ํน์ ๋ฆฌ์์ค์ ๊ด๋ จ๋ ๊ท์น๋ ์ถฉ์กฑํด์ผ ํฉ๋๋ค.
์ฐ๋ฆฌ๋ ์ ์ฑ ๊ธฐ๋ฐ ์ ๊ทผ ๋ฐฉ์์ผ๋ก ์ด๋ฅผ ๋ชจ๋ธ๋งํ ์ ์์ต๋๋ค. ํน์ ๊ถํ์ ํด๋นํ๋ ์ ์ฑ ๋งต์ ์ ์ํฉ๋๋ค.
// src/policies.ts
import { User } from './user';
// Define our resource types
type Post = { id: string; authorId: string; };
// Define a map of policies. The keys are our type-safe permissions!
type PolicyMap = {
[Permissions.POST.UPDATE]?: (user: User, post: Post) => boolean;
[Permissions.POST.DELETE]?: (user: User, post: Post) => boolean;
// Other policies...
};
export const policies: PolicyMap = {
[Permissions.POST.UPDATE]: (user, post) => {
// To update a post, the user must be the author.
return user.id === post.authorId;
},
[Permissions.POST.DELETE]: (user, post) => {
// To delete a post, the user must be the author.
return user.id === post.authorId;
},
};
// We can create a new, more powerful check function
export function can(user: User | null, permission: AllPermissions, resource?: any): boolean {
if (!user) return false;
// 1. First, check if the user has the basic permission from their role.
if (!hasPermission(user, permission)) {
return false;
}
// 2. Next, check if a specific policy exists for this permission.
const policy = policies[permission];
if (policy) {
// 3. If a policy exists, it must be satisfied.
if (!resource) {
// The policy requires a resource, but none was provided.
console.warn(`Policy for ${permission} was not checked because no resource was provided.`);
return false;
}
return policy(user, resource);
}
// 4. If no policy exists, having the role-based permission is enough.
return true;
}
์ด์ ์ฐ๋ฆฌ API ์๋ํฌ์ธํธ๋ ๋ ๋ฏธ๋ฌํ๊ณ ์์ ํด์ง๋๋ค:
import { can } from './policies';
import { Permissions } from './permissions';
app.put('/api/posts/:id', async (req, res) => {
const currentUser = req.user;
const post = await db.posts.findById(req.params.id);
// Check the ability to update this *specific* post
if (can(currentUser, Permissions.POST.UPDATE, post)) {
// User has the 'post:update' permission AND is the author.
// Proceed with update logic...
} else {
res.status(403).send({ error: 'You are not authorized to update this post.' });
}
});
ํ๋ก ํธ์๋ ํตํฉ: ๋ฐฑ์๋์ ํ๋ก ํธ์๋ ๊ฐ์ ํ์ ๊ณต์
์ด ์ ๊ทผ ๋ฐฉ์์ ๊ฐ์ฅ ์ค์ํ ์ด์ ์ค ํ๋๋ ํนํ ํ๋ก ํธ์๋์ ๋ฐฑ์๋ ๋ชจ๋์์ TypeScript๋ฅผ ์ฌ์ฉํ ๋ ์ด๋ฌํ ํ์ ์ ๊ณต์ ํ ์ ์๋ค๋ ๊ฒ์ ๋๋ค. `permissions.ts`, `roles.ts` ๋ฐ ๊ธฐํ ๊ณต์ ํ์ผ์ ๋ชจ๋ ธ๋ ํฌ(Nx, Turborepo ๋๋ Lerna์ ๊ฐ์ ๋๊ตฌ ์ฌ์ฉ) ๋ด์ ๊ณตํต ํจํค์ง์ ๋ฐฐ์นํ๋ฉด ํ๋ก ํธ์๋ ์ ํ๋ฆฌ์ผ์ด์ ์ด ๊ถํ ๋ถ์ฌ ๋ชจ๋ธ์ ์์ ํ ์ธ์ํ๊ฒ ๋ฉ๋๋ค.
์ด๋ฅผ ํตํด ์ฌ์ฉ์์ ๊ถํ์ ๋ฐ๋ผ ์์๋ฅผ ์กฐ๊ฑด๋ถ๋ก ๋ ๋๋งํ๋ ๊ฒ๊ณผ ๊ฐ์ ๊ฐ๋ ฅํ ํจํด์ UI ์ฝ๋์์ ์ฌ์ฉํ ์ ์์ผ๋ฉฐ, ์ด ๋ชจ๋ ๊ฒ์ ํ์ ์์คํ ์ ์์ ์ฑ๊ณผ ํจ๊ป ์ ๊ณต๋ฉ๋๋ค.
React ๊ตฌ์ฑ ์์๋ฅผ ์๊ฐํด ๋ด ์๋ค:
// In a React component
import { Permissions } from '@my-app/shared-types'; // Importing from a shared package
import { useAuth } from './auth-context'; // A custom hook for authentication state
interface EditPostButtonProps {
post: Post;
}
const EditPostButton = ({ post }: EditPostButtonProps) => {
const { user, can } = useAuth(); // 'can' is a hook using our new policy-based logic
// The check is type-safe. The UI knows about permissions and policies!
if (!can(user, Permissions.POST.UPDATE, post)) {
return null; // Don't even render the button if the user can't perform the action
}
return ;
};
์ด๊ฒ์ ํ๋๋ฅผ ๋ฐ๊พธ๋ ์ผ์ ๋๋ค. ํ๋ก ํธ์๋ ์ฝ๋๋ ๋ ์ด์ UI ๊ฐ์์ฑ์ ์ ์ดํ๊ธฐ ์ํด ์ถ์ธกํ๊ฑฐ๋ ํ๋์ฝ๋ฉ๋ ๋ฌธ์์ด์ ์ฌ์ฉํ ํ์๊ฐ ์์ต๋๋ค. ๋ฐฑ์๋์ ๋ณด์ ๋ชจ๋ธ๊ณผ ์๋ฒฝํ๊ฒ ๋๊ธฐํ๋๋ฉฐ, ๋ฐฑ์๋์ ๊ถํ ๋ณ๊ฒฝ ์ฌํญ์ ์ ๋ฐ์ดํธ๋์ง ์์ผ๋ฉด ํ๋ก ํธ์๋์์ ์ฆ์ ํ์ ์ค๋ฅ๋ฅผ ๋ฐ์์์ผ UI ๋ถ์ผ์น๋ฅผ ๋ฐฉ์งํฉ๋๋ค.
๋น์ฆ๋์ค ์ฌ๋ก: ์กฐ์ง์ด ํ์ ์์ ์ฑ ๊ถํ ๋ถ์ฌ์ ํฌ์ํด์ผ ํ๋ ์ด์
์ด ํจํด์ ์ฑํํ๋ ๊ฒ์ ๋จ์ํ ๊ธฐ์ ์ ๊ฐ์ ์ด์์ ๋๋ค. ์ด๋ ์ค์ง์ ์ธ ๋น์ฆ๋์ค ์ด์ ์ ์ ๊ณตํ๋ ์ ๋ต์ ํฌ์์ ๋๋ค.
- ๋ฒ๊ทธ์ ๋ํญ ๊ฐ์: ๊ถํ ๋ถ์ฌ์ ๊ด๋ จ๋ ์ ์ฒด ์ ํ์ ๋ณด์ ์ทจ์ฝ์ ๋ฐ ๋ฐํ์ ์ค๋ฅ๋ฅผ ์ ๊ฑฐํฉ๋๋ค. ์ด๋ ๋ ์์ ์ ์ธ ์ ํ๊ณผ ๋น์ฉ์ด ๋ง์ด ๋๋ ํ๋ก๋์ ์ฌ๊ณ ๊ฐ์๋ก ์ด์ด์ง๋๋ค.
- ๊ฐ๋ฐ ์๋ ๊ฐ์ํ: ์๋ ์์ฑ, ์ ์ ๋ถ์ ๋ฐ ์์ฒด ๋ฌธ์ํ ์ฝ๋๋ ๊ฐ๋ฐ์๋ฅผ ๋ ๋น ๋ฅด๊ณ ์์ ๊ฐ ์๊ฒ ๋ง๋ญ๋๋ค. ๊ถํ ๋ฌธ์์ด์ ์ฐพ๊ฑฐ๋ ์กฐ์ฉํ ๊ถํ ๋ถ์ฌ ์คํจ๋ฅผ ๋๋ฒ๊น ํ๋ ๋ฐ ์๊ฐ์ด ๋ ์์๋ฉ๋๋ค.
- ๊ฐ์ํ๋ ์จ๋ณด๋ฉ ๋ฐ ์ ์ง๋ณด์: ๊ถํ ์์คํ ์ ๋ ์ด์ ๋ถ์กฑ์ ์ง์์ด ์๋๋๋ค. ์๋ก์ด ๊ฐ๋ฐ์๋ ๊ณต์ ๋ ํ์ ์ ๊ฒ์ฌํ์ฌ ๋ณด์ ๋ชจ๋ธ์ ์ฆ์ ์ดํดํ ์ ์์ต๋๋ค. ์ ์ง๋ณด์ ๋ฐ ๋ฆฌํฉํ ๋ง์ ์ํ์ด ๋ฎ๊ณ ์์ธก ๊ฐ๋ฅํ ์์ ์ด ๋ฉ๋๋ค.
- ํฅ์๋ ๋ณด์ ์ํ: ๋ช ํํ๊ณ ๋ช ์์ ์ด๋ฉฐ ์ค์์์ ๊ด๋ฆฌ๋๋ ๊ถํ ์์คํ ์ ๊ฐ์ฌํ๊ณ ์ถ๋ก ํ๊ธฐ ํจ์ฌ ์ฝ์ต๋๋ค. "๋๊ฐ ์ฌ์ฉ์๋ฅผ ์ญ์ ํ ๊ถํ์ ๊ฐ์ง๊ณ ์๋๊ฐ?"์ ๊ฐ์ ์ง๋ฌธ์ ๋ตํ๋ ๊ฒ์ด ์ฌ์ํ ์ผ์ด ๋ฉ๋๋ค. ์ด๋ ๊ท์ ์ค์ ๋ฐ ๋ณด์ ๊ฒํ ๋ฅผ ๊ฐํํฉ๋๋ค.
๋์ ๊ณผ์ ๋ฐ ๊ณ ๋ ค ์ฌํญ
๊ฐ๋ ฅํ์ง๋ง, ์ด ์ ๊ทผ ๋ฐฉ์์๋ ๋ช ๊ฐ์ง ๊ณ ๋ ค ์ฌํญ์ด ์์ต๋๋ค:
- ์ด๊ธฐ ์ค์ ๋ณต์ก์ฑ: ์ฝ๋ ์ ์ฒด์ ๋จ์ํ ๋ฌธ์์ด ๊ฒ์ฌ๋ฅผ ๋ถ์ฐํ๋ ๊ฒ๋ณด๋ค ๋ ๋ง์ ์ฌ์ ์ํคํ ์ฒ์ ์ฌ๊ณ ๊ฐ ํ์ํฉ๋๋ค. ๊ทธ๋ฌ๋ ์ด ์ด๊ธฐ ํฌ์๋ ํ๋ก์ ํธ์ ์ ์ฒด ์๋ช ์ฃผ๊ธฐ ๋์ ์ด์ต์ ๊ฐ์ ธ๋ค์ค๋๋ค.
- ๊ท๋ชจ์์์ ์ฑ๋ฅ: ์์ฒ ๊ฐ์ ๊ถํ์ด๋ ๊ทน๋๋ก ๋ณต์กํ ์ฌ์ฉ์ ๊ณ์ธต ๊ตฌ์กฐ๋ฅผ ๊ฐ์ง ์์คํ ์์ ์ฌ์ฉ์ ๊ถํ ์งํฉ(`getUserPermissions`)์ ๊ณ์ฐํ๋ ํ๋ก์ธ์ค๊ฐ ๋ณ๋ชฉ ํ์์ด ๋ ์ ์์ต๋๋ค. ์ด๋ฌํ ์๋๋ฆฌ์ค์์๋ ์บ์ฑ ์ ๋ต(์: ๊ณ์ฐ๋ ๊ถํ ์งํฉ์ ์ ์ฅํ๊ธฐ ์ํด Redis ์ฌ์ฉ)์ ๊ตฌํํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค.
- ๋๊ตฌ ๋ฐ ์ธ์ด ์ง์: ์ด ์ ๊ทผ ๋ฐฉ์์ ๋ชจ๋ ์ด์ ์ ๊ฐ๋ ฅํ ์ ์ ํ์ดํ ์์คํ ์ ๊ฐ์ง ์ธ์ด์์ ์คํ๋ฉ๋๋ค. Python ๋๋ Ruby์ ๊ฐ์ ๋์ ํ์ ์ธ์ด์์ ํ์ ํํ ๋ฐ ์ ์ ๋ถ์ ๋๊ตฌ๋ฅผ ์ฌ์ฉํ์ฌ ์ ์ฌํ๊ฒ ๊ตฌํํ ์ ์์ง๋ง, TypeScript, C#, Java, Rust์ ๊ฐ์ ์ธ์ด์ ๊ฐ์ฅ ์ ํฉํฉ๋๋ค.
๊ฒฐ๋ก : ๋ ์์ ํ๊ณ ์ ์ง๋ณด์ ๊ฐ๋ฅํ ๋ฏธ๋ ๊ตฌ์ถ
์ฐ๋ฆฌ๋ ๋งค์ง ๋ฌธ์์ด์ ์ํํ ์งํ์์ ํ์ ์์ ์ฑ ๊ถํ ๋ถ์ฌ๋ผ๋ ์ ์์ํ๋ ๋์๋ก ์ฌํํ์ต๋๋ค. ๊ถํ์ ๋จ์ํ ๋ฐ์ดํฐ๊ฐ ์๋๋ผ ์ ํ๋ฆฌ์ผ์ด์ ํ์ ์์คํ ์ ํต์ฌ ๋ถ๋ถ์ผ๋ก ์ทจ๊ธํจ์ผ๋ก์จ ์ปดํ์ผ๋ฌ๋ฅผ ๋จ์ํ ์ฝ๋ ๊ฒ์ฌ๊ธฐ์์ ๊ฒฝ๊ณํ๋ ๋ณด์ ์์์ผ๋ก ๋ณํ์ํต๋๋ค.
ํ์ ์์ ์ฑ ๊ถํ ๋ถ์ฌ๋ ๊ฐ๋ฐ ์๋ช ์ฃผ๊ธฐ์์ ๊ฐ๋ฅํ ํ ์ผ์ฐ ์ค๋ฅ๋ฅผ ํฌ์ฐฉํ๋ ํ๋ ์ํํธ์จ์ด ๊ณตํ ์์น์ธ '์ฌํํธ ๋ ํํธ'์ ์ฆ๊ฑฐ์ ๋๋ค. ์ด๋ ์ฝ๋ ํ์ง, ๊ฐ๋ฐ์ ์์ฐ์ฑ, ๊ทธ๋ฆฌ๊ณ ๊ฐ์ฅ ์ค์ํ๊ฒ๋ ์ ํ๋ฆฌ์ผ์ด์ ๋ณด์์ ๋ํ ์ ๋ต์ ํฌ์์ ๋๋ค. ์์ฒด ๋ฌธ์ํ๋๊ณ , ๋ฆฌํฉํ ๋งํ๊ธฐ ์ฌ์ฐ๋ฉฐ, ์ค์ฉํ ์ ์๋ ์์คํ ์ ๊ตฌ์ถํจ์ผ๋ก์จ ๋ ๋์ ์ฝ๋๋ฅผ ์์ฑํ๋ ๊ฒ์ ๋์ด ์ ํ๋ฆฌ์ผ์ด์ ๊ณผ ํ์ ์ํ ๋ ์์ ํ๊ณ ์ ์ง๋ณด์ ๊ฐ๋ฅํ ๋ฏธ๋๋ฅผ ๊ตฌ์ถํ๋ ๊ฒ์ ๋๋ค. ๋ค์ ๋ฒ์ ์ ํ๋ก์ ํธ๋ฅผ ์์ํ๊ฑฐ๋ ๊ธฐ์กด ํ๋ก์ ํธ๋ฅผ ๋ฆฌํฉํ ๋งํ ๋ ์ค์ค๋ก์๊ฒ ๋ฌผ์ด๋ณด์ธ์: ๋น์ ์ ๊ถํ ๋ถ์ฌ ์์คํ ์ ๋น์ ์ ์ํด ์ผํ๊ณ ์์ต๋๊น, ์๋๋ฉด ๋น์ ์๊ฒ ๋ถ๋ฆฌํ๊ฒ ์์ฉํ๊ณ ์์ต๋๊น?